/*
 * This file is Copyright 2002-2006 Loyola University Chicago,
 * Department of Computer Science and the Emerging Technologies Laboratory.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License via
 *    http://www.apache.org/licenses/LICENSE-2.0.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.citep.tests.db.hibernate.sessions;

import java.util.Hashtable;
import java.util.Date;
import java.util.Calendar;
import java.util.List;
import java.math.BigDecimal;

import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.hibernate3.HibernateObjectRetrievalFailureException;
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;

import com.citep.business.sessions.*;
import com.citep.formats.input.transactions.*;
import com.citep.business.*;
import com.citep.db.hibernate.TransactionDAO;
import com.citep.db.hibernate.EntryDAO;


import junit.framework.TestCase;

public class TransactionSessionTest extends AbstractTransactionalDataSourceSpringContextTests {

	private TransactionSession transactionSession;
	private UserSession userSession;
	private AccountSession accountSession;
	private TransactionImportContext transactionImport;
	
	private static User user;
	private static int transactionId;
	private static int entryId;

	private static Hashtable<String,Account> testAccounts = new Hashtable<String,Account>();
	
	public void setTransactionSession(TransactionSession s) {
		transactionSession = s;
	}
	
	public void setAccountSession(AccountSession s) {
		accountSession = s;
	}
	
	public void setUserSession(UserSession s) {
		userSession = s;
	}
	
	public void setTransactionImport(TransactionImportContext s) {
		transactionImport = s;
	}
	
	protected String[] getConfigLocations() {
		return new String[] { "classpath:applicationContext.xml" };
	}	

	public void testInit() {
		Role role = userSession.createRole("Admin", "Administrator", true);
		user = userSession.createUser("test user", "test last name", "test password", "test email", role);
		AccountCategory category = accountSession.createAccountCategory("account category test", "account category description test");
		
		testAccounts.put("test_account",accountSession.createAccount("account test", "account description test",AccountTypes.EXPENSES, new java.math.BigDecimal("1000"),new Date(), new Date(),true, category));
		
		setComplete();
	}
	
	public void testCreateTransactionUserDateString() {
		Transaction transaction = transactionSession.createTransaction(user, new Date(), "test memo");
		assertNotNull(transaction);
		assertTrue(transaction.getId() > 0);
		transactionId = transaction.getId();
		setComplete();
	}
	
	
	public void testGetTransaction() {
		Transaction transaction = transactionSession.getTransaction(transactionId);
		assertNotNull(transaction);
		assertEquals(transaction.getId(),transactionId);
	}

	public void testCreateTransactionTransaction() {
		Transaction transaction = new TransactionDAO();
		transaction.setMemo("test memo");
		transaction.setUser(user);
		transaction.setTransactionTime(new Date());
		
		transactionSession.createTransaction(transaction);
		
		assertTrue(transaction.getId() > 0);
		setComplete();
	}


	public void testUpdateTransaction() {
		Transaction transaction = transactionSession.getTransaction(transactionId);
		transaction.setMemo("memo test 2");
		transactionSession.updateTransaction(transaction);
		
		assertEquals(transaction.getMemo(),"memo test 2");
	}


	public void testGetTransactions() {
		List<Transaction> transactions = transactionSession.getTransactions();
		
		assertEquals(transactions.size(),2);
		assertEquals(transactions.iterator().next().getId(),transactionId);
	}

	public void testGetTransactionsUser() {
		List<Transaction> transactions = transactionSession.getTransactions(user);
		assertEquals(transactions.size(),2);
		assertEquals(transactions.iterator().next().getId(),transactionId);
	}

	public void testGetTransactionsDateDate() {
		
		Calendar startDate = Calendar.getInstance();
		startDate.set(2000, 1, 1);
		
		Calendar endDate = Calendar.getInstance();
		endDate.set(2000,1,1);
		
		List<Transaction> transactions = transactionSession.getTransactions(startDate.getTime(),endDate.getTime());
		assertEquals(transactions.size(),0);
		
		endDate.set(2010,1,1);

		transactions = transactionSession.getTransactions(startDate.getTime(),endDate.getTime());
		assertEquals(transactions.size(),2);
	}
	
	public void testTransactionAddEntries() {
		EntryDAO entry = new EntryDAO();
		entry.setAmount(new java.math.BigDecimal(10.00));
		entry.setMemo("credit to account");
		entry.setType(EntryTypes.CREDIT);
		entry.setAccount(testAccounts.get("test_accounts"));
		
		Transaction transaction = transactionSession.createTransaction(user, new Date("1/1/2000"), "memo");
		assertNotNull(transaction);

		transaction.getEntries().add(entry);
		entry.setTransaction(transaction);
		
		transactionSession.updateTransaction(transaction);
		
		setComplete();
	}
	
	public void testGetEntriesUser() {
		List<Entry> entries = transactionSession.getEntries(user);
		
		assertNotNull(entries);
		System.out.println(entries.size());
		assertEquals(1,entries.size());
	}

	public void testGetEntriesDateDate() {
		Calendar startDate = Calendar.getInstance();
		startDate.set(2000, 1, 1);
		
		Calendar endDate = Calendar.getInstance();
		endDate.set(2000,1,1);
		
		List<Entry> entries = transactionSession.getEntries(startDate.getTime(),endDate.getTime());
		assertEquals(entries.size(),0);
		
		endDate.set(2010,1,1);

		entries = transactionSession.getEntries(startDate.getTime(),endDate.getTime());
		assertEquals(entries.size(),1);
	}

	public void testGetEntriesAccount() {
		List<Entry> entries = transactionSession.getEntries(testAccounts.get("test_account"),0,0);
		assertEquals(entries.size(),1);
	}

	public void testDeleteTransactionInt() {
		transactionSession.deleteTransaction(transactionId);
		setComplete();
	}
	
	public void testGetTransactionNone() {
		try {
			System.out.println(transactionSession.getTransaction(transactionId).getId());
			fail("exception expected");
		} catch (ObjectRetrievalFailureException e) {
			assertEquals(HibernateObjectRetrievalFailureException.class,e.getClass());
		}
	}

	/*
	 * Test account transfer, part 1: Create accounts
	 */
	public void testTransferCreateAccounts() {
		AccountCategory category = accountSession.createAccountCategory("category", "category");
		
		//save these in our usedAccounts array to later delete when subsequent tests are complete
		testAccounts.put("checking", accountSession.createAccount("Checking", "Checking account", AccountTypes.ASSETS, new BigDecimal("0.00"), new Date(), new Date(), true, category));
		testAccounts.put("cash",accountSession.createAccount("Cash", "Cash", AccountTypes.ASSETS, new BigDecimal("0.00"), new Date(), new Date(), true, category));
		testAccounts.put("groceries",accountSession.createAccount("Groceries", "Groceries", AccountTypes.EXPENSES, new BigDecimal("0.00"), new Date(), new Date(), true, category));
		testAccounts.put("starting balances", accountSession.createAccount("Starting Balances", "Equity account", AccountTypes.EQUITY, new BigDecimal("0.00"), new Date(), new Date(), true, category));
		
		setComplete();
	}
	
	/*
	 * Test transfers between accounts
	 */
	public void testTransfer() {
		
		//set a starting balance for the checking account
		System.out.println("transfer 1");
		transactionSession.transfer(user, testAccounts.get("checking"), testAccounts.get("starting balances"), EntryTypes.DEBIT, "Starting balance for checking account", new Date(), new BigDecimal("1000.00"));
		
		//transfer 10 dollars to the cash account
		System.out.println("transfer 2");
		transactionSession.transfer(user, testAccounts.get("checking"), testAccounts.get("cash"), EntryTypes.CREDIT, "Transfer to cash", new Date(), new BigDecimal("10.00"));
		
		//do some grocery shopping with the checking account ($20)
		transactionSession.transfer(user, testAccounts.get("checking"), testAccounts.get("groceries"), EntryTypes.CREDIT, "Buy some groceries", new Date(), new BigDecimal("20.00"));
		setComplete();
	}
	
	/*
	 * Test that the balances are correct
	 */
	public void testGetBalance() {
		
		Transaction lastTransaction = null;
		List<Transaction> accountTransactions;
		
		accountTransactions = transactionSession.getTransactions(testAccounts.get("checking"), 0, 0);
		
		System.out.println("Transactions:" + accountTransactions.size());
		
		//the balance on the checking account should now be 970
		for(Transaction t:accountTransactions)
			lastTransaction = t;
		
		assertTrue(lastTransaction != null);
		
		assertEquals(transactionSession.getAccountBalance(testAccounts.get("checking"), lastTransaction),new BigDecimal("970.00"));
		assertEquals(testAccounts.get("cash").getCurrentBalance(),new BigDecimal("10.00"));
		assertEquals(testAccounts.get("starting balances").getCurrentBalance(), new BigDecimal("1000.00"));
		assertEquals(testAccounts.get("groceries").getCurrentBalance(), new BigDecimal("20.00"));
		
		System.out.println("Checking balance: " + testAccounts.get("checking").getCurrentBalance());
		System.out.println("Cash balance: " + testAccounts.get("cash").getCurrentBalance());
		System.out.println("Equity balance:" + testAccounts.get("starting balances").getCurrentBalance());
		System.out.println("Expenses (groceries) balance:" + testAccounts.get("groceries").getCurrentBalance());
	}
}
